"""
文本比较查看器组件
实现并排显示文本差异
"""

from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QSplitter, QTextEdit,
    QPushButton, QLabel, QComboBox, QCheckBox, QGroupBox,
    QFileDialog, QMessageBox, QScrollArea, QFrame, QPlainTextEdit
)
from PyQt6.QtCore import Qt, pyqtSignal, QRect, QSize
from PyQt6.QtGui import QFont, QTextCursor, QTextCharFormat, QColor, QPalette, QPainter, QTextFormat

from ..core.text_comparator import TextComparator
from ..core.file_handler import FileHandler


class DiffLineNumberArea(QWidget):
    """比较视图的行号显示区域"""
    
    def __init__(self, editor):
        super().__init__(editor)
        self.editor = editor
        
    def sizeHint(self):
        return QSize(self.editor.line_number_area_width(), 0)
    
    def paintEvent(self, event):
        self.editor.line_number_area_paint_event(event)


class DiffTextEditor(QPlainTextEdit):
    """带行号显示的文本编辑器，用于比较视图"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.line_number_area = DiffLineNumberArea(self)
        self.sync_editor = None  # 同步滚动的另一个编辑器
        
        # 连接信号
        self.blockCountChanged.connect(self.update_line_number_area_width)
        self.updateRequest.connect(self.update_line_number_area)
        self.verticalScrollBar().valueChanged.connect(self.on_scroll_changed)
        
        # 初始化
        self.update_line_number_area_width(0)
        
    def line_number_area_width(self):
        """计算行号区域宽度"""
        digits = 1
        max_num = max(1, self.blockCount())
        while max_num >= 10:
            max_num //= 10
            digits += 1
        
        space = 3 + self.fontMetrics().horizontalAdvance('9') * digits
        return space
    
    def update_line_number_area_width(self, _):
        """更新行号区域宽度"""
        self.setViewportMargins(self.line_number_area_width(), 0, 0, 0)
    
    def update_line_number_area(self, rect, dy):
        """更新行号区域"""
        if dy:
            self.line_number_area.scroll(0, dy)
        else:
            self.line_number_area.update(0, rect.y(), self.line_number_area.width(), rect.height())
        
        if rect.contains(self.viewport().rect()):
            self.update_line_number_area_width(0)
    
    def resizeEvent(self, event):
        """重写大小改变事件"""
        super().resizeEvent(event)
        
        cr = self.contentsRect()
        self.line_number_area.setGeometry(QRect(cr.left(), cr.top(),
                                               self.line_number_area_width(), cr.height()))
    
    def line_number_area_paint_event(self, event):
        """绘制行号区域"""
        painter = QPainter(self.line_number_area)
        painter.fillRect(event.rect(), QColor("#f0f0f0"))
        
        block = self.firstVisibleBlock()
        block_number = block.blockNumber()
        top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
        bottom = top + self.blockBoundingRect(block).height()
        
        while block.isValid() and top <= event.rect().bottom():
            if block.isVisible() and bottom >= event.rect().top():
                number = str(block_number + 1)
                painter.setPen(QColor("#666666"))
                painter.drawText(QRect(0, int(top), self.line_number_area.width() - 5, 
                               self.fontMetrics().height()),
                               Qt.AlignmentFlag.AlignRight, number)
            
            block = block.next()
            top = bottom
            bottom = top + self.blockBoundingRect(block).height()
            block_number += 1
    
    def on_scroll_changed(self, value):
        """滚动条值改变时同步另一个编辑器"""
        if self.sync_editor and hasattr(self.sync_editor, 'verticalScrollBar'):
            # 临时断开信号连接，避免循环调用
            self.sync_editor.verticalScrollBar().valueChanged.disconnect(self.sync_editor.on_scroll_changed)
            self.sync_editor.verticalScrollBar().setValue(value)
            self.sync_editor.verticalScrollBar().valueChanged.connect(self.sync_editor.on_scroll_changed)


class DiffViewer(QWidget):
    """文本比较查看器"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.text_comparator = TextComparator()
        self.file_handler = FileHandler()
        
        self.left_text = ""
        self.right_text = ""
        self.diff_result = None
        
        # 同步滚动控制
        self.sync_scrolling = True
        
        self.init_ui()

    def init_ui(self):
        """初始化用户界面"""
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)  # 移除边距
        layout.setSpacing(0)  # 移除间距

        # 中间分割器 - 占据所有可用空间
        self.splitter = QSplitter(Qt.Orientation.Horizontal)
        layout.addWidget(self.splitter, 1)  # 设置拉伸因子为1

        # 左侧文本编辑器
        self.left_editor = self.create_text_editor("左侧文本")
        self.splitter.addWidget(self.left_editor)

        # 右侧文本编辑器
        self.right_editor = self.create_text_editor("右侧文本")
        self.splitter.addWidget(self.right_editor)

        # 连接同步滚动信号
        self.setup_sync_scrolling()
        
        # 搜索对话框
        self.search_dialog = None

    def setup_sync_scrolling(self):
        """设置同步滚动"""
        # 获取实际的编辑器组件
        left_editor = self.left_editor.findChild(QPlainTextEdit)
        right_editor = self.right_editor.findChild(QPlainTextEdit)
        
        if left_editor and right_editor:
            # 设置相互引用
            left_editor.sync_editor = right_editor
            right_editor.sync_editor = left_editor

    # 移除不再需要的控制面板创建方法
    # def create_control_panel(self):
    #     """创建控制面板"""
    #     # 此方法已不再使用，比较选项已移到工具栏
    #     pass

    def create_text_editor(self, title):
        """创建文本编辑器"""
        container = QWidget()
        layout = QVBoxLayout(container)
        layout.setContentsMargins(0, 0, 0, 0)  # 移除边距
        layout.setSpacing(0)  # 移除间距

        # 标题
        title_label = QLabel(title)
        title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        title_label.setStyleSheet("font-weight: bold; padding: 5px; background-color: #f0f0f0; border-bottom: 1px solid #ccc;")
        title_label.setFixedHeight(30)
        layout.addWidget(title_label, 0)  # 不拉伸

        # 文本编辑器 - 占据剩余空间
        editor = DiffTextEditor()
        editor.setFont(QFont("Consolas", 10))
        editor.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
        layout.addWidget(editor, 1)  # 设置拉伸因子为1

        return container

    def create_status_panel(self):
        """创建状态面板"""
        panel = QWidget()
        panel.setFixedHeight(25)  # 固定状态栏高度
        layout = QHBoxLayout(panel)
        layout.setContentsMargins(5, 2, 5, 2)  # 减少边距

        self.status_label = QLabel("就绪")
        self.stats_label = QLabel("")

        layout.addWidget(self.status_label)
        layout.addStretch()
        layout.addWidget(self.stats_label)

        return panel

    def load_file(self, side):
        """加载文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, f"加载{side}侧文件", "",
            "文本文件 (*.txt *.md *.py *.js *.json *.xml *.html *.css *.sql);;所有文件 (*)"
        )

        if file_path:
            try:
                content = self.file_handler.read_file(file_path)
                if side == 'left':
                    self.left_editor.findChild(QPlainTextEdit).setPlainText(content)
                    self.left_text = content
                else:
                    self.right_editor.findChild(QPlainTextEdit).setPlainText(content)
                    self.right_text = content

                self.status_label.setText(f"已加载{side}侧文件: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法加载文件: {str(e)}")

    def save_file(self, side):
        """保存文件"""
        file_path, _ = QFileDialog.getSaveFileName(
            self, f"保存{side}侧文件", "",
            "文本文件 (*.txt);;所有文件 (*)"
        )

        if file_path:
            try:
                if side == 'left':
                    content = self.left_editor.findChild(QPlainTextEdit).toPlainText()
                else:
                    content = self.right_editor.findChild(QPlainTextEdit).toPlainText()
                
                self.file_handler.write_file(file_path, content)
                self.status_label.setText(f"已保存{side}侧文件: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法保存文件: {str(e)}")
    
    def compare_texts(self):
        """比较文本"""
        # 获取文本内容
        self.left_text = self.left_editor.findChild(QPlainTextEdit).toPlainText()
        self.right_text = self.right_editor.findChild(QPlainTextEdit).toPlainText()
        
        if not self.left_text and not self.right_text:
            QMessageBox.warning(self, "警告", "请先输入要比较的文本")
            return
        
        # 执行比较（比较选项已通过set_comparison_options设置）
        self.diff_result = self.text_comparator.compare_texts(self.left_text, self.right_text)
        
        # 显示差异
        self.display_diff()
        
        # 更新状态
        stats = self.diff_result['statistics']
        similarity = self.diff_result['similarity_ratio']
        self.stats_label.setText(
            f"相似度: {similarity:.2%} | "
            f"总行数: {stats['total_lines']} | "
            f"添加: {stats['added_lines']} | "
            f"删除: {stats['removed_lines']} | "
            f"相同: {stats['unchanged_lines']}"
        )
        
        self.status_label.setText("比较完成")
    
    def display_diff(self):
        """显示差异"""
        if not self.diff_result:
            return
        
        # 清空编辑器
        left_editor = self.left_editor.findChild(QTextEdit)
        right_editor = self.right_editor.findChild(QTextEdit)
        
        left_editor.clear()
        right_editor.clear()
        
        # 设置差异格式
        added_format = QTextCharFormat()
        added_format.setBackground(QColor("#C8E6C9"))  # 浅绿色
        added_format.setForeground(QColor("#2E7D32"))
        
        removed_format = QTextCharFormat()
        removed_format.setBackground(QColor("#FFCDD2"))  # 浅红色
        removed_format.setForeground(QColor("#C62828"))
        
        unchanged_format = QTextCharFormat()
        unchanged_format.setBackground(QColor("#FFFFFF"))  # 白色
        unchanged_format.setForeground(QColor("#000000"))
        
        # 显示差异结果
        diff_result = self.diff_result['diff_result']
        
        for result in diff_result:
            if result.line_type == 'unchanged':
                # 相同的行，在两个编辑器中都显示
                left_editor.append(result.content)
                right_editor.append(result.content)
                
                # 设置格式
                cursor = left_editor.textCursor()
                cursor.movePosition(QTextCursor.MoveOperation.End)
                cursor.movePosition(QTextCursor.MoveOperation.StartOfLine, QTextCursor.MoveMode.KeepAnchor)
                cursor.mergeCharFormat(unchanged_format)
                
                cursor = right_editor.textCursor()
                cursor.movePosition(QTextCursor.MoveOperation.End)
                cursor.movePosition(QTextCursor.MoveOperation.StartOfLine, QTextCursor.MoveMode.KeepAnchor)
                cursor.mergeCharFormat(unchanged_format)
                
            elif result.line_type == 'removed':
                # 删除的行，只在左侧显示
                left_editor.append(result.content)
                
                cursor = left_editor.textCursor()
                cursor.movePosition(QTextCursor.MoveOperation.End)
                cursor.movePosition(QTextCursor.MoveOperation.StartOfLine, QTextCursor.MoveMode.KeepAnchor)
                cursor.mergeCharFormat(removed_format)
                
            elif result.line_type == 'added':
                # 添加的行，只在右侧显示
                right_editor.append(result.content)
                
                cursor = right_editor.textCursor()
                cursor.movePosition(QTextCursor.MoveOperation.End)
                cursor.movePosition(QTextCursor.MoveOperation.StartOfLine, QTextCursor.MoveMode.KeepAnchor)
                cursor.mergeCharFormat(added_format)
    
    def merge_texts(self):
        """合并文本"""
        if not self.left_text and not self.right_text:
            QMessageBox.warning(self, "警告", "请先输入要合并的文本")
            return
        
        # 显示合并选项对话框
        merge_strategy = self.show_merge_dialog()
        if merge_strategy:
            merged_text = self.text_comparator.merge_texts(
                self.left_text, self.right_text, merge_strategy
            )
            
            # 将合并结果放入左侧编辑器
            self.left_editor.findChild(QTextEdit).setPlainText(merged_text)
            self.left_text = merged_text
            
            self.status_label.setText(f"已合并文本 (策略: {merge_strategy})")
    
    def show_merge_dialog(self):
        """显示合并选项对话框"""
        from PyQt6.QtWidgets import QDialog, QVBoxLayout, QRadioButton, QButtonGroup, QDialogButtonBox
        
        dialog = QDialog(self)
        dialog.setWindowTitle("选择合并策略")
        dialog.setModal(True)
        
        layout = QVBoxLayout(dialog)
        
        # 合并选项
        append_radio = QRadioButton("追加 (将右侧文本添加到左侧末尾)")
        prepend_radio = QRadioButton("前置 (将右侧文本添加到左侧开头)")
        smart_radio = QRadioButton("智能合并 (去除重复行)")
        
        append_radio.setChecked(True)
        
        layout.addWidget(append_radio)
        layout.addWidget(prepend_radio)
        layout.addWidget(smart_radio)
        
        # 按钮
        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        button_box.accepted.connect(dialog.accept)
        button_box.rejected.connect(dialog.reject)
        layout.addWidget(button_box)
        
        if dialog.exec() == QDialog.DialogCode.Accepted:
            if append_radio.isChecked():
                return 'append'
            elif prepend_radio.isChecked():
                return 'prepend'
            elif smart_radio.isChecked():
                return 'smart'
        
        return None
    
    def clear_texts(self):
        """清空文本"""
        self.left_editor.findChild(QTextEdit).clear()
        self.right_editor.findChild(QTextEdit).clear()
        self.left_text = ""
        self.right_text = ""
        self.diff_result = None
        self.status_label.setText("已清空")
        self.stats_label.setText("")
    
    def set_comparison_options(self, ignore_case=False, ignore_whitespace=False, ignore_empty_lines=False):
        """设置比较选项"""
        self.text_comparator.set_comparison_options(ignore_case, ignore_whitespace, ignore_empty_lines)
    
    def resizeEvent(self, event):
        """窗口大小改变事件"""
        super().resizeEvent(event)
        # 确保分割器比例保持1:1
        if hasattr(self, 'splitter'):
            total_width = self.splitter.width()
            half_width = total_width // 2
            self.splitter.setSizes([half_width, half_width])
    
    def get_unified_diff(self):
        """获取统一格式的差异"""
        if not self.left_text or not self.right_text:
            return ""
        
        return self.text_comparator.generate_unified_diff(self.left_text, self.right_text)
    
    def export_diff_report(self):
        """导出差异报告"""
        if not self.diff_result:
            QMessageBox.warning(self, "警告", "请先执行文本比较")
            return
        
        file_path, _ = QFileDialog.getSaveFileName(
            self, "导出差异报告", "", 
            "文本文件 (*.txt);;HTML文件 (*.html)"
        )
        
        if file_path:
            try:
                if file_path.endswith('.html'):
                    content = self.text_comparator.generate_html_diff(self.left_text, self.right_text)
                else:
                    content = self.get_unified_diff()
                
                self.file_handler.write_file(file_path, content)
                self.status_label.setText(f"已导出差异报告: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法导出报告: {str(e)}")
    
    # 搜索和替换功能
    def show_search_dialog(self, side='left'):
        """显示搜索对话框"""
        if not self.search_dialog:
            from .search_dialog import SearchDialog
            self.search_dialog = SearchDialog(self)
            self.search_dialog.find_next.connect(lambda *args: self.find_next(*args, side))
            self.search_dialog.find_previous.connect(lambda *args: self.find_previous(*args, side))
            self.search_dialog.find_all.connect(lambda *args: self.find_all(*args, side))
            self.search_dialog.replace.connect(lambda *args: self.replace_text(*args, side))
            self.search_dialog.replace_all.connect(lambda *args: self.replace_all_text(*args, side))
        
        self.search_dialog.show()
        self.search_dialog.raise_()
        self.search_dialog.activateWindow()
    
    def find_next(self, search_text, case_sensitive, whole_word, side='left'):
        """查找下一个"""
        editor = self.get_editor_by_side(side)
        if not editor:
            return
            
        found = editor.find_text(search_text, case_sensitive, whole_word, True)
        if found:
            self.search_dialog.set_status(f"在{side}侧找到: {search_text}")
        else:
            self.search_dialog.set_status(f"在{side}侧未找到: {search_text}")
    
    def find_previous(self, search_text, case_sensitive, whole_word, side='left'):
        """查找上一个"""
        editor = self.get_editor_by_side(side)
        if not editor:
            return
            
        found = editor.find_text(search_text, case_sensitive, whole_word, False)
        if found:
            self.search_dialog.set_status(f"在{side}侧找到: {search_text}")
        else:
            self.search_dialog.set_status(f"在{side}侧未找到: {search_text}")
    
    def find_all(self, search_text, case_sensitive, whole_word, side='left'):
        """查找所有"""
        editor = self.get_editor_by_side(side)
        if not editor:
            return
            
        matches = editor.find_all(search_text, case_sensitive, whole_word)
        if matches:
            self.search_dialog.set_status(f"在{side}侧找到 {len(matches)} 个匹配项")
        else:
            self.search_dialog.set_status(f"在{side}侧未找到: {search_text}")
    
    def replace_text(self, search_text, replace_text, case_sensitive, whole_word, side='left'):
        """替换当前匹配项"""
        editor = self.get_editor_by_side(side)
        if not editor:
            return
            
        replaced = editor.replace_text(search_text, replace_text, case_sensitive, whole_word)
        if replaced:
            self.search_dialog.set_status(f"在{side}侧已替换: {search_text} -> {replace_text}")
        else:
            self.search_dialog.set_status(f"在{side}侧未找到匹配项: {search_text}")
    
    def replace_all_text(self, search_text, replace_text, case_sensitive, whole_word, side='left'):
        """替换所有匹配项"""
        editor = self.get_editor_by_side(side)
        if not editor:
            return
            
        count = editor.replace_all(search_text, replace_text, case_sensitive, whole_word)
        if count > 0:
            self.search_dialog.set_status(f"在{side}侧已替换 {count} 个匹配项")
        else:
            self.search_dialog.set_status(f"在{side}侧未找到匹配项: {search_text}")
    
    def get_editor_by_side(self, side):
        """根据侧边获取编辑器"""
        if side == 'left':
            return self.left_editor.findChild(QPlainTextEdit)
        elif side == 'right':
            return self.right_editor.findChild(QPlainTextEdit)
        return None 